// promise 内部状态常量定义
const PENDING = 'penduing'; // 等待状态
const FULFILLED = 'fulfilled'; // 成功状态
const REJECTED = 'rejected'; // 失败状态

class MyPromise {

    constructor(exctor) {
        // 捕获执行器内部发生的异常
        try {
            exctor(this.resovle, this.rejected);
        } catch (error) {
            this.reject(error)
        }
    }

    // promise 对象状态，默认创建promise后为等待状态
    status = PENDING;
    // 成功的值
    successValue = undefined;
    // 失败的值
    rejectValue = undefined;
    // 用户注册的成功回调方法
    successCallbacks = [];
    // 用户注册的失败回调方法
    rejectCallbacks = [];

    /**
     * 将promise的状态改为成功
     * @param val 成功返回的值
     */
    resovle = (val) => {
        // 先判断是、状态是否是等待，只有等待状态下、才能修改
        if (this.status !== PENDING) return;
        // 修改状态，改为成功
        this.status = FULFILLED;
        // 保存成功的值
        this.successValue = val;
        // 判读之前是否注册的有成功回调方法，如果有、依次调用
        while (this.successCallbacks.length) {
            this.successCallbacks.shift()()
        }
    }

    /**
     * 将promise的状态改为失败
     * @param reason 失败原因
     */
    rejected = (reason) => {
        // 先判断是、状态是否是等待，只有等待状态下、才能修改
        if (this.status !== PENDING) return;
        // 修改状态 改为失败
        this.status = REJECTED;
        // 保存失败的原因
        this.rejectValue = reason;
        // 判断之前注册的是否有失败回调，如果有，依次调用
        while (this.rejectCallbacks.length) {
            this.rejectCallbacks.shift()();
        }
    }

    /**
     * 注册成功、失败的回调方法
     * @param successCb 成功回调
     * @param rejectCb 失败回调
     */
    then = (successCb = val => val, rejectCb = reason => { throw reason }) => {
        // rejectCb = rejectCb ? rejectCb : reason => { throw reason }
        // 返回一个新的promise对象，实现链式调用
        let linkPromise = new MyPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                // 成功
                // 设置定时器原因是把下面的代码放到异步队列，等linkpromise对象生成完成之后再调用，因为resovlePromise方法里需要拿到linkpromise对象
                setTimeout(() => {
                    try {
                        let resl = successCb(this.successValue)
                        resolvePromse(linkPromise, resl, resolve, reject)
                    } catch (error) {
                        // 如果上述代码发生异常，调用reject
                        reject(error)
                    }
                }, 0);
            } else if (this.status === REJECTED) {
                // 失败
                setTimeout(() => {
                    try {
                        let reason = rejectCb(this.rejectValue)
                        resolvePromse(linkPromise, reason, resolve, reject)
                    } catch (error) {
                        // 如果上述代码发生异常，调用reject
                        reject(error)
                    }
                }, 0);
                
                
            } else {
                // 当前处于等待状态，现将成功及失败回调保存起
                this.successCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let resl = successCb(this.successValue);
                            resolvePromse(linkPromise, resl, resolve, reject);
                        } catch (error) {
                            // 如果上述代码发生异常，调用reject
                            reject(error)
                        }
                    }, 0);
                    
                    

                });
                this.rejectCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let reason = rejectCb(this.rejectValue);
                            resolvePromse(linkPromise, reason, resolve, reject);
                        } catch (error) {
                            // 如果上述代码发生异常，调用reject
                            reject(error)
                        }
                        
                    }, 0);
                });
            }
        })
        
        return linkPromise;
        
    } 

    /**
     * finally 方法中注册的回调不管最后promise是成功还是失败都会执行，
     * 实现思路是： 调用当前promise对象的then方法，注册成功、失败回调，不管哪个方法被执行，都调用用户传递进来的callback
     */
    finally = callback => {
        this.then(value => {
            MyPromise.resolve(callback()).then(() => value)
        }, reason => {
            MyPromise.resolve(callback()).then(() => { throw reason})
        })
    }

    /**
     * catch 方法用来捕获异常，实现思路： 调用then方法，主动注册一个失败回调， 当有错误发生时、就会执行callback
     */
    catch = callback => {
         return this.then(undefined, callback)
    }

    // 静态resolve方法，该方法可以生成一个promise对象
    static resolve = value => {
        // 判断传进来的值是否是promise对象，如果已经是promise、直接返回
        if (value instanceof MyPromise) return value;
        // 如果不是，创建一个新的返回，并将状态设为成功
        return new MyPromise((resolve, reject) => resolve(value))
    }

    // all方法，用来执行一组异步操作，当所有操作都执行完成后、才返回成功
    static all = array => {
        const result = []; // 用来记录每个异步任务的执行结果
        const index = 0; // 用来统计有多少个异步任务已经成功执行
        return new MyPromise((resolve, reject) => {
            function addData(key, value) {
                result[key] = value;
                index++;
                if (index === array.length) {
                    resolve(result);
                }
            }
            // 循环调用每一异步任务
            for (let i = 0; i < array.length; i++) {
                let current = array[i];
                if (current instanceof MyPromise) {
                    current.then(value => addData(i, value), reason => reject(reason))
                } else {
                    // 普通值
                    addData(i, array[i]);
                }
            }
        })
    }

    // race 方法，接收一组异步操作，最先完成的异步操作可以执行回调，其他异步任务会丢弃
    static race = array => {
        return new MyPromise( (resolve, reject) => {
            for (let i = 0; i < array.length; i++) {
                MyPromise.resolve(array[i]).then(data => {
                    resolve(data);
                }, err => {
                    return reject(err)
                })
            }
        })
    }

}


/**
 * 
 * @param {MyPromise} linkPromise 链式调用生成的新promise对象
 * @param {*} resl // 会函数返回的结果
 * @param {Function} resolve 
 * @param {Function} reject
 */
function resolvePromse(linkPromise, resl, resolve, reject) {
    // 判断返回的结果是否等于当前promise
    if (linkPromise === resl) {
        // 如果是，则返回报错信息
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }
    // 判断如果返回的结果已经是promise对象，直接调用then方法
    if (resl instanceof MyPromise) {
        resl.then(resolve, reject)
    } else {
        // 返回的结果不是promise对象
        resolve(resl);
    }
}


module.exports = MyPromise;